/* CSP-201609P4 交通规划（图、最短路）
时间限制：	1.0s
内存限制：	256.0MB

- 连通性不需考虑：总可以从S乘车到首都再到T。
- 求最小改造长度：不是最小生成树，是求总长最小的最短路径树。
  如果路径等长、但是最后一边更短的话，也要松弛。
  相当于需改造的新边更短、更多重用了路径之前的改造部分。
教训：要先对样例手算一遍在编程。手算之后就会发现与普通的最短路不一样。
*/
#include <climits>
#include <iostream>
#include <list>
#include <queue>
#include <vector>
using namespace std;

struct edge{
  int u, v, w;
  edge(int tu, int tv, int tw): u(tu), v(tv), w(tw) {}

  edge reverse() const{
    edge e(*this);
    swap(e.u, e.v);
    return e;
  }
};

bool operator<(const edge& a, const edge& b){
  return a.w > b.w;
}

struct Graph{
  const int nv, ne;
  vector<list<edge> > va;
  vector<int> distTo; // 从源点到v的最短路径长
  vector<int> lastTo; // 从源点到v的最短路径末边距离

  Graph(int nvertex, int nedge)
    : nv(nvertex + 1), ne(nedge),
      va(nv), distTo(nv), lastTo(nv)
  {
    edge e(0,0,0);
    for(int i = 0; i < ne; ++i){
      cin >> e.u >> e.v >> e.w;
      va[e.u].push_back(e);
      va[e.v].push_back(e.reverse());
    }
  }

  void relax(int u, priority_queue<edge>& pq){
    for(list<edge>::const_iterator p = va[u].begin(), pe = va[u].end(); p != pe; ++p){
      const edge& e = *p;
      int dv = distTo[u] + e.w;
      if((distTo[e.v] > dv) || (distTo[e.v] == dv && lastTo[e.v] > e.w)){
        // 借助新边，路径更短、或路径持平但新边较短
        distTo[e.v] = dv;
        lastTo[e.v] = e.w;
        pq.push(edge(e.v, -1, dv));
        //cout << "relax edge: " << e.u << ' ' << e.v << ' ' << e.w << ' ' << dv << endl;
      }
    }
  }

  void dijkstra(){
    distTo.assign(nv, INT_MAX);
    distTo[1] = 0;
    lastTo.assign(nv, 0);

    priority_queue<edge> pq;
    pq.push(edge(1,-1,0));
    while(!pq.empty()){
      edge e = pq.top();
      pq.pop();
      relax(e.u, pq);
    }
    int spw = 0;
    for(int v = 1; v < nv; ++v){
      spw += lastTo[v];
    }
    cout << spw << endl;
  }
};

int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  int n, m;
  cin >> n >> m;
  Graph g(n, m);
  g.dijkstra();
  return 0;
}
